Packet capture for only certain TCP flags (for use with Fortigate 'sniffer' command):

Information extracted from the Man pages of 'tcpdump' command:

# Updated Jan 2007 for Fortinet KC by Stephen Dennis.

       There are 8 bits in the control bits section of the TCP header:

       Let's  assume  that  we  want  to  watch  packets  used  in establishing a TCP
       connection.  Recall that TCP uses a 3-way handshake protocol when it  initial-
       izes  a new connection; the connection sequence with regard to the TCP control
       bits is

              1) Caller sends SYN
              2) Recipient responds with SYN, ACK
              3) Caller sends ACK

       Now we're interested in capturing packets that have only the SYN bit set (Step
       1).   Note that we don't want packets from step 2 (SYN-ACK), just a plain ini-
       tial SYN.  What we need is a correct filter expression for tcpdump.

       Recall the structure of a TCP header without options:

        0                            15                              31
       -----------------------------------------------------------------
       |          source port          |       destination port        |
       -----------------------------------------------------------------
       |                        sequence number                        |
       -----------------------------------------------------------------
       |                     acknowledgment number                     |
       -----------------------------------------------------------------
       |  HL   | rsvd  |C|E|U|A|P|R|S|F|        window size            |
       -----------------------------------------------------------------
       |         TCP checksum          |       urgent pointer          |
       -----------------------------------------------------------------

       A TCP header usually holds 20 octets of data, unless options are present.  The
       first  line of the graph contains octets 0 - 3, the second line shows octets 4
       - 7 etc.

       Starting to count with 0, the relevant TCP control bits are contained in octet
       13:

        0             7|             15|             23|             31
       ----------------|---------------|---------------|----------------
       |  HL   | rsvd  |C|E|U|A|P|R|S|F|        window size            |
       ----------------|---------------|---------------|----------------
       |               |  14th octet   |               |               |

       Let's have a closer look at octet no. 13:

                       |               |
                       |---------------|
                       |C|E|U|A|P|R|S|F| <- TCP (and ECN) flags
                       |---------------|
                       |7   5   3     0| <- bit position

       These  are  the  TCP  control bits we are interested in.  We have numbered the
       bits in this octet from 0 to 7, right to left, so the PSH bit is bit number 3 
       (4th bit from right), while the URG bit is number 5 (6th bit from right).

       Recall that we want to capture packets with only SYN set.  Let's see what hap-
       pens to octet 13 if a TCP datagram arrives with the SYN bit set in its header:

                       |C|E|U|A|P|R|S|F| <- TCP and ECN flags
                       |---------------|
                       |0 0 0 0 0 0 1 0| <- bit value
                       |---------------|
                       |7 6 5 4 3 2 1 0| <- bit position

       Looking  at  the  control  bits section we see that only bit number 1 (SYN) is
       set.

       Assuming that octet number 13 is an 8-bit unsigned  integer  in  network  byte
       order, the binary value of this octet is

              00000010

       and its decimal representation is:

             | bit  | dec.   | bit   |       | TCP
             | pos. | value  | value | total | Flag
         ____|______|________|_______|_______|_____
             |      |        |       |       |
         2   ^   7  =  128,  x   0   =   0   | CWR (ECN Congestion Window Reduced)
         2   ^   6  =   64,  x   0   =   0   | ECE (ECN Capable Echo)
         2   ^   5  =   32,  x   0   =   0   | URG
         2   ^   4  =   16,  x   0   =   0   | ACK
         2   ^   3  =    8,  x   0   =   0   | PSH
         2   ^   2  =    4,  x   0   =   0   | RST
         2   ^   1  =    2,  x   1   =   2   | SYN*
         2   ^   0  =    1,  x   0   =   0   | FIN
         ____|______|________|_______|_______|_____
                                  sum:   2
         ==========================================
       ('2 ^ 7 = 128' means '2 to the power of 7 equals 128')

       We're  almost  done, because now we know that if only SYN is set, the value of
       the 14th octet in the TCP header, when interpreted as an 8-bit unsigned integer
       in network byte order, must be exactly 2.

       This relationship can be expressed as
              'tcp[13]==2'

       The expression says "match all TCP datagrams where octet 13 (i.e. the 14th octet)
       is equal to the decimal value 2", which is exactly what we want.

       But now, let's assume that we need to capture ALL packets with the  SYN bit  set,
       regardless of whether any other TCP control bits (such as ACK, URG, PSH etc.) are
       set. Let's see what happens to octet 13 when a TCP datagram arrives with both the
       SYN and ACK bits set:

            |C|E|U|A|P|R|S|F| <- TCP (and ECN) flags
            |---------------|
            |0 0 0 1 0 0 1 0| <- bit value
            |---------------|
            |7 6 5 4 3 2 1 0| <- bit position

       Now bits 4 and 1 are set in the 14th octet.  The binary value of octet 13 is now:

                   00010010

       which translates to a decimal value of:


             | bit  | dec.   | bit   |       | TCP
             | pos. | value  | value | total | Flag
         ____|______|________|_______|____________
             |      |        |       |       |
         2   ^   7  =  128,  x   0   =   0   | CWR (ECN Congestion Window Reduced)
         2   ^   6  =   64,  x   0   =   0   | ECE (ECN Capable Echo)
         2   ^   5  =   32,  x   0   =   0   | URG
         2   ^   4  =   16,  x   1   =  16   | ACK*
         2   ^   3  =    8,  x   0   =   0   | PSH
         2   ^   2  =    4,  x   0   =   0   | RST
         2   ^   1  =    2,  x   1   =   2   | SYN*
         2   ^   0  =    1,  x   0   =   0   | FIN
         ____|______|________|_______|_______|_____
                                  sum:  18
         ==========================================

       We can't just use 'tcp[13]==18' in the tcpdump filter expression, because that
       would match only those packets where both SYN and ACK are set, but not packets
       with only SYN set. Remember that we don't care if ACK or any other control bit
       is set, as long as SYN is set.

       In order to achieve our goal, we need to logically 'AND' the binary  value  of
       octet  13 with some other value to preserve the SYN bit.  We know that we want
       SYN to be set in any case, so we'll logically AND the value in the 14th  octet
       with the binary value of a SYN:

                       v                             v
                 00010010 SYN-ACK              00000010 SYN
            AND  00000010 (we want SYN)   AND  00000010 (we want SYN)
                 --------                      --------
            =    00000010                 =    00000010
                       ^                             ^
       We see that this AND operation delivers the same result regardless of  whether
       any other TCP control bits are set or not. The decimal representation  of  the
       AND value as well as the result of this operation is 2 (binary 00000010), so we
       know that for packets with SYN set the following relation must hold true:

              ( ( value of octet 13 ) AND ( 2 ) ) == ( 2 )

       This relationship can be expressed as:

       'tcp[13]&2==2'

       The following list of expressions expand on this idea:

       'tcp[13]==1'       matches packets with only the FIN bit set to 1;
       'tcp[13]&4==4'     matches all packet with the RST bit set to 1;
       'tcp[13]&8==8'     matches all packet with the PSH bit set to 1;
       'tcp[13]==16'      matches packets with only the ACK bit set to 1;
       'tcp[13]&32==32'   matches all packets with the URG bit set to 1;
       'tcp[13]&64==64'   matches all packets with the ECE bit set to 1;
       'tcp[13]&128==128' matches all packets with the CWR bit set to 1;
       'tcp[13]==24'      matches packets with only the PSH and ACK bits set to 1;


Fortigate examples:
--------------------

Example to sniff for packets with ONLY the TCP SYN set (no other flags set):

# diag sniff packet wan1 'tcp[13]==2'


Example to sniff for ANY packets with the TCP SYN set (regardless of other flags):

# diag sniff packet wan1 'tcp[13]&2==2'


Example to sniff for any packets on port 80 (HTTP) with the TCP SYN set:

# diag sniff packet wan1 'port 80 and tcp[13]&2==2'


Example to sniff for any packets on port 80 (HTTP) or 443 (HTTPS) with the TCP SYN set:

# diag sniffer packet internal 'port 80 or 443 and tcp[13]&2==2'


